﻿//***************************************************************
// 脚本名称：UISpriteManager
// 类创建人：周  波
// 创建日期：2015.12
// 功能描述：管理界面动态替换的图片资源
//***************************************************************
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class UISpriteManager : Kernel<UISpriteManager>
{
    /// <summary>
    /// 是否已经初始化成功
    /// </summary>
    public bool IsInited { get; private set; }

    /// <summary>
    /// 已经加载的Sprite资源
    /// </summary>
    Dictionary<string, Sprite> m_LoadedUISpriteDict = new Dictionary<string, Sprite>();

    /// <summary>
    /// 正在加载的资源列表
    /// </summary>
    List<LoadingInfo> m_LoadingList = new List<LoadingInfo>();

    /// <summary>
    /// 是否包含UI图标
    /// </summary>
    /// <param name="spriteName"></param>
    /// <returns></returns>
    public bool ContainsUISprite(string spriteName)
    {
        return m_LoadedUISpriteDict.ContainsKey(spriteName);
    }

    /// <summary>
    /// 获取UI Sprite
    /// （推荐使用GetUISprite 方法获取)
    /// </summary>
    /// <param name="spriteName">资源名称</param>
    /// <returns>如果已经加载则返回Sprite ，否则返回 false</returns>
    public Sprite GetUISpriteWhitOutLoad(string spriteName)
    {
        return m_LoadedUISpriteDict.ContainsKey(spriteName) ? m_LoadedUISpriteDict[spriteName] : null;
    }

    /// <summary>
    /// 获取UI Sprite
    /// </summary>
    /// <param name="spriteName">资源名称</param>
    /// <returns></returns>
    public UISpriteOperation GetUISprite(string spriteName)
    {
        UISpriteOperation operation = new UISpriteOperation(spriteName);

        if (string.IsNullOrEmpty(spriteName))
        {
            operation.SetLoadedResult(null, "Sprite name can't be null or Empty, please check it!");
            return operation;
        }

        if (m_LoadedUISpriteDict.ContainsKey(spriteName)) // 已经加载好了资源
        {
            operation.SetLoadedResult(m_LoadedUISpriteDict[spriteName]);
            return operation;
        }
        else
        {
            string prefabName = GetSpritePrefabName(spriteName);
            // 检查待加载的资源是否正在加载中...
            LoadingInfo loadingInfo = m_LoadingList.Find(temp => temp.AssetName == prefabName);
            if (loadingInfo != null)
            {
                loadingInfo.UISpriteOperationList.Add(operation);
                return operation;
            }
            else
            {
                AssetBundleManager assetBundleManager = AssetBundleManager.Instance();
                if (assetBundleManager != null)
                {
                    AssetBundleLoadAssetOperation loadAssetOperation = assetBundleManager.LoadAssetAsync(prefabName, typeof(GameObject));
                    if (loadAssetOperation != null)
                    {
                        loadingInfo = new LoadingInfo(prefabName);
                        loadingInfo.LoadAssetOperation = loadAssetOperation;
                        loadingInfo.UISpriteOperationList.Add(operation);
                        m_LoadingList.Add(loadingInfo);
                    }
                    else
                    {
                        operation.SetLoadedResult(null, string.Format("Can't load Asset[{0}] when your want to load Sprite[{1}], please check and fix this error!", prefabName, spriteName));
                    }
                    return operation;
                }
                else
                {
                    operation.SetLoadedResult(null, string.Format("AssetBundleManager was null when you want to load UISprite[{0}]", spriteName));
                    return operation;
                }
            }
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    string GetSpritePrefabName(string spriteName)
    {
        return spriteName;
    }

    void Update()
    {
        // 处理正在加载的资源信息
        if (m_LoadingList.Count > 0)
        {
            for (int index = m_LoadingList.Count - 1; index >= 0; index--)
            {
                LoadingInfo loadingInfo = m_LoadingList[index];
                if (loadingInfo.LoadAssetOperation.IsDone())
                {
                    HandleLoadingDoneItem(loadingInfo);

                    m_LoadingList.RemoveAt(index);
                }
                else
                {
                    continue;
                }
            }
        }
    }

    /// <summary>
    /// 处理已经加载完成的元素
    /// </summary>
    /// <param name="loadingInfo"></param>
    private void HandleLoadingDoneItem(LoadingInfo loadingInfo)
    {
        GameObject resource = loadingInfo.LoadAssetOperation.GetAsset<GameObject>();
        if (resource == null)
        {
            string errorMessage = string.Format("Can't load Asset[{0}], please check and fix this error!", loadingInfo.AssetName);
            for (int i = 0; i < loadingInfo.UISpriteOperationList.Count; i++)
            {
                loadingInfo.UISpriteOperationList[i].SetLoadedResult(null, errorMessage);
            }
        }
        else
        {
            ParseSpritesToDict(resource);

            // 返回给各个等待器
            for (int i = 0; i < loadingInfo.UISpriteOperationList.Count; i++)
            {
                UISpriteOperation operation = loadingInfo.UISpriteOperationList[i];
                if (m_LoadedUISpriteDict.ContainsKey(operation.SpriteName))
                {
                    operation.SetLoadedResult(m_LoadedUISpriteDict[operation.SpriteName], string.Empty);
                }
                else
                {
                    string errorMessage = string.Format("Load Sprite Error, the prefab[{0}] don't have sprite whit name[{1}]", loadingInfo.AssetName, operation.SpriteName);
                    operation.SetLoadedResult(null, errorMessage);
                }
            }
        }
    }

    /// <summary>
    /// 解析Sprite 到已加载的字典中
    /// </summary>
    /// <param name="resource"></param>
    /// <remarks>只解析两种情况的图片资源：1、图片资源放置在跟节点上，2、图片资源放置在子节点上</remarks>
    void ParseSpritesToDict(GameObject resource)
    {
        if (resource == null)
            return;

        SpriteRenderer uiSpriteConfig = resource.GetComponent<SpriteRenderer>();
        if (uiSpriteConfig != null)
        {
            if (uiSpriteConfig.sprite != null)
            {
                if (!m_LoadedUISpriteDict.ContainsKey(uiSpriteConfig.sprite.name))
                    m_LoadedUISpriteDict.Add(uiSpriteConfig.sprite.name, uiSpriteConfig.sprite);
            }
            else
            {
                Debug.LogErrorFormat("Prefab[{0}]'s SpriteRenderer hava null sprite, please check it and fix!", resource.name);
            }
        }
        else
        {
            Debug.LogErrorFormat("Prefab[{0}] don't have SpriteRenderer script, please check your prefab!", resource.name);
        }
    }

    /// <summary>
    /// 设置动态设置图片的 Sprite
    /// </summary>
    /// <param name="image">图片控件</param>
    /// <param name="spriteName">图片名称</param>
    /// <returns></returns>
    public void SetImageSprite(UnityEngine.UI.Image image, string spriteName, bool isNativeSize = false)
    {
        StartCoroutine(SetImageSpriteByName(image, spriteName, isNativeSize));
    }

    /// <summary>
    /// 设置动态设置图片的 Sprite
    /// </summary>
    /// <param name="image">图片控件</param>
    /// <param name="spriteName">图片名称</param>
    /// <param name="isNetiveSize"></param>
    /// <returns></returns>
    public IEnumerator SetImageSpriteByName(UnityEngine.UI.Image image, string spriteName, bool isNetiveSize = false)
    {
        UISpriteOperation operation = UISpriteManager.Instance().GetUISprite(spriteName);
        if (!operation.IsDone)
        {
            yield return operation;
        }

        if (string.IsNullOrEmpty(operation.ErrorMessage))
        {
            if (image != null)
            {
                image.sprite = operation.UISprite;
                if (isNetiveSize)
                {
                    image.SetNativeSize();
                }
            }
        }
        else
        {
            Debug.LogError(operation.ErrorMessage);
        }
    }

    /// <summary>
    /// 正在加载的资源信息
    /// </summary>
    class LoadingInfo
    {
        public LoadingInfo(string prefabAssetName)
        {
            this.AssetName = prefabAssetName;
        }

        public string AssetName { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        public AssetBundleLoadAssetOperation LoadAssetOperation { get; set; }

        private readonly List<UISpriteOperation> m_UISpriteOperationList = new List<UISpriteOperation>();
        /// <summary>
        /// 
        /// </summary>
        public List<UISpriteOperation> UISpriteOperationList
        {
            get { return m_UISpriteOperationList; }
        }
    }

}

public class UISpriteOperation : CustomYieldInstruction
{
    /// <summary>
    /// 资源结果
    /// </summary>
    public Sprite UISprite { get; private set; }
    /// <summary>
    /// 资源名称
    /// </summary>
    public string SpriteName { get; private set; }

    /// <summary>
    /// 是否加载完成
    /// </summary>
    public bool IsDone { get; private set; }

    /// <summary>
    /// 错误信息
    /// </summary>
    public string ErrorMessage { get; private set; }

    /// <summary>
    /// 设置加载结果
    /// </summary>
    /// <param name="sprite"></param>
    /// <param name="errorMessage"></param>
    public void SetLoadedResult(Sprite sprite, string errorMessage = "")
    {
        this.UISprite = sprite;
        this.IsDone = true;
        this.ErrorMessage = errorMessage;
    }

    public override bool keepWaiting
    {
        get
        {
            return !IsDone;
        }
    }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="bundleName">资源包名</param>
    /// <param name="assetName">资源名</param>
    /// <param name="type">资源类型</param>
    public UISpriteOperation(string spriteName)
    {
        this.SpriteName = spriteName;
        this.UISprite = null;
        this.IsDone = false;
        this.ErrorMessage = string.Empty;
    }
}
